﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Amazon.DynamoDB;
using Amazon.DynamoDB.Model;
using Amazon.DynamoDB.DataModel;
using Amazon.DynamoDB.DocumentModel;
using Amazon.Runtime;
using Amazon.SecurityToken;
using DevToolBox.Cloud.Common;

namespace DevToolBox.Cloud.Amazon
{
    /// <summary>
    /// Method to encapsulate the Amazon DynamoDb Operations (not trasactions)
    /// </summary>
    public class DynamoTableOperations : DynamoDbClient, IManageable
    {

        /// <summary>
        /// Default constructor
        /// </summary>
        public DynamoTableOperations() :base()
        {
        }

        /// <summary>
        /// Method to create a set of tables
        /// </summary>
        /// <param name="configObject">List of TableInfo with the information to create the tables</param>
        public void Create(object configObject)
        {
            if (configObject == null)
            {
                return;
            }

            TableInfo table = (TableInfo)configObject;
            List<string> currentTables = Client.ListTables().ListTablesResult.TableNames;
            bool tablesAdded = false;

            if (!currentTables.Contains(table.Name))
            {
                Client.CreateTable(new CreateTableRequest
                {
                    TableName = table.Name,
                    ProvisionedThroughput = new ProvisionedThroughput
                    {
                        ReadCapacityUnits = ReadCapacityUnits,
                        WriteCapacityUnits = WriteCapacityUnits
                    },
                    KeySchema = new KeySchema
                    {
                        HashKeyElement = new KeySchemaElement
                        {
                            AttributeName = table.HashKey,
                            AttributeType = table.HashKeyType
                        },
                        RangeKeyElement = new KeySchemaElement
                        {
                            AttributeName = table.RangeKey,
                            AttributeType = table.RangeKeyType
                        }
                    }
                });
                tablesAdded = true;
            }

            if (tablesAdded)
            {
                bool isTableActive = false;
                string tableStatus = null;
                while (!isTableActive)
                {
                    tableStatus = GetStatus(table.Name);
                    isTableActive = string.Equals(tableStatus, "ACTIVE", 
                                           StringComparison.OrdinalIgnoreCase);
                    Thread.Sleep(TimeSpan.FromSeconds(5));
                }
            }
        }

        /// <summary>
        /// Get the table's status
        /// </summary>
        /// <param name="key">Table name</param>
        /// <returns>Status string</returns>
        public string GetStatus(string key)
        {
            try
            {
                var table = Client.DescribeTable(new DescribeTableRequest { TableName = key }).DescribeTableResult.Table;
                return (table == null) ? string.Empty : table.TableStatus;
            }
            catch (AmazonDynamoDBException db)
            {
                if (db.ErrorCode == "ResourceNotFoundException")
                    return string.Empty;
                throw;
            }
        }

        /// <summary>
        /// Frees unmanaged resources
        /// </summary>
        public void Dispose()
        {
            if (Client != null)
            {
                Client.Dispose();
            }
        }

        /// <summary>
        /// Method to delete a table
        /// </summary>
        /// <param name="key">Table name or key</param>
        public void Delete(string key)
        {
            if (key == null)
            {
                return;
            }

            List<string> currentTables = Client.ListTables().ListTablesResult.TableNames;
            bool tableDeleted = false;

            if (currentTables.Contains(key))
            {
                var deleteRequest = new DeleteTableRequest();
                deleteRequest.TableName = key;
                Client.DeleteTable(deleteRequest);
                tableDeleted = true;
            }

            if (tableDeleted)
            {
                bool tableExists = true;
                while (tableExists)
                {
                    string tableStatus = GetStatus(key);
                    tableExists = string.Equals(tableStatus, "DELETING", StringComparison.OrdinalIgnoreCase);
                    Thread.Sleep(TimeSpan.FromSeconds(5));
                }
            }
        }

        public void Update(object configObject)
        {
            string[] parameters = (string[])configObject;

            if (parameters != null && parameters.Length == 3)
            {
                int readUnit;
                int.TryParse(parameters[1], out readUnit);
                int writeUnit;
                int.TryParse(parameters[2], out writeUnit);

                var updateRequest = new UpdateTableRequest();
                updateRequest.TableName = parameters[0];
                updateRequest.ProvisionedThroughput = new ProvisionedThroughput();
                updateRequest.ProvisionedThroughput.ReadCapacityUnits = readUnit;
                updateRequest.ProvisionedThroughput.WriteCapacityUnits = writeUnit;
                Client.UpdateTable(updateRequest);
            }
        }
    }
}
